--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit d167d7c32e1b16a30a90867cd5880bb679673e54
Parents : 1788aea
Author : Sudo-Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-01-14T18:00:02-06:00
Update legacy Electron configuration and build tasks
- Add legacy package.json
- Update Taskfile
Changes
3 files changed, 210 insertions(+), 14 deletions(-)
Diff
diff --git a/Taskfile.yml b/Taskfile.yml
index e075d5a1..7b8dbfa7 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -289,27 +289,51 @@ tasks:
- task: build-appimage
electron-legacy:
- desc: Install legacy Electron version
+ desc: Ensure legacy Electron version is available (will be downloaded by electron-builder if needed)
cmds:
- - "{{.NPM}} install --no-save electron@{{.LEGACY_ELECTRON_VERSION}}"
+ - 'echo "Building with legacy Electron version: {{.LEGACY_ELECTRON_VERSION}}"'
build-appimage-legacy:
desc: Build Linux AppImage with legacy Electron version
deps: [build-frontend, electron-legacy]
cmds:
- - "{{.NPM}} run electron-postinstall"
+ - "npx electron-builder install-app-deps --config package-legacy.json"
- "PLATFORM=linux {{.NPM}} run build-backend"
- - "{{.NPM}} run dist -- --linux AppImage -c.extraMetadata.main=electron/main-legacy.js"
- - "./scripts/rename_legacy_artifacts.sh"
+ - "npx electron-builder --config package-legacy.json --linux AppImage --publish=never"
build-exe-legacy:
desc: Build Windows portable executable with legacy Electron version
deps: [build-frontend, electron-legacy]
cmds:
- - "{{.NPM}} run electron-postinstall"
+ - "npx electron-builder install-app-deps --config package-legacy.json"
+ - "PLATFORM=win32 {{.NPM}} run build-backend"
+ - "npx electron-builder --config package-legacy.json --win portable --publish=never"
+
+ build-exe-legacy-wine:
+ desc: Build Windows portable executable and NSIS installer using Wine (Legacy)
+ deps: [build-frontend, electron-legacy]
+ cmds:
+ - "npx electron-builder install-app-deps --config package-legacy.json"
+ - "PLATFORM=win32 PYTHON_CMD='{{.WINE_PYTHON}}' {{.NPM}} run build-backend"
+ - "npx electron-builder --config package-legacy.json --win portable nsis --publish=never"
+
+ build-legacy-all:
+ desc: Build all legacy Electron apps (Linux and Windows)
+ deps: [build-frontend, electron-legacy]
+ cmds:
+ - "npx electron-builder install-app-deps --config package-legacy.json"
+ - "PLATFORM=linux {{.NPM}} run build-backend"
- "PLATFORM=win32 {{.NPM}} run build-backend"
- - "{{.NPM}} run dist -- --win portable -c.extraMetadata.main=electron/main-legacy.js"
- - "./scripts/rename_legacy_artifacts.sh"
+ - "npx electron-builder --config package-legacy.json --linux AppImage deb --win portable nsis --publish=never"
+
+ build-legacy-all-wine:
+ desc: Build all legacy Electron apps (Linux + Windows via Wine)
+ deps: [build-frontend, electron-legacy]
+ cmds:
+ - "npx electron-builder install-app-deps --config package-legacy.json"
+ - "PLATFORM=linux {{.NPM}} run build-backend"
+ - "PLATFORM=win32 PYTHON_CMD='{{.WINE_PYTHON}}' {{.NPM}} run build-backend"
+ - "npx electron-builder --config package-legacy.json --linux AppImage deb --win portable nsis --publish=never"
forge-start:
desc: Run the application with Electron Forge
diff --git a/electron/main-legacy.js b/electron/main-legacy.js
index 9679e033..f6e9528c 100644
--- a/electron/main-legacy.js
+++ b/electron/main-legacy.js
@@ -1,15 +1,76 @@
-const { app, BrowserWindow, dialog, ipcMain, shell, systemPreferences } = require("electron");
+const {
+ app,
+ BrowserWindow,
+ dialog,
+ ipcMain,
+ shell,
+ systemPreferences,
+ Notification,
+ powerSaveBlocker,
+} = require("electron");
const electronPrompt = require("electron-prompt");
const { spawn } = require("child_process");
const fs = require("fs");
const path = require("node:path");
+const crypto = require("crypto");
// remember main window
var mainWindow = null;
+// power save blocker id
+var activePowerSaveBlockerId = null;
+
// remember child process for exe so we can kill it when app exits
var exeChildProcess = null;
+// store integrity status
+var integrityStatus = {
+ backend: { ok: true, issues: [] },
+ data: { ok: true, issues: [] },
+};
+
+function verifyBackendIntegrity(exeDir) {
+ const manifestPath = path.join(exeDir, "backend-manifest.json");
+ if (!fs.existsSync(manifestPath)) {
+ log("Backend integrity manifest missing, skipping check.");
+ return { ok: true, issues: ["Manifest missing"] };
+ }
+
+ try {
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
+ const issues = [];
+
+ const filesToVerify = manifest.files || manifest;
+ const metadata = manifest._metadata || {};
+
+ for (const [relPath, expectedHash] of Object.entries(filesToVerify)) {
+ const fullPath = path.join(exeDir, relPath);
+ if (!fs.existsSync(fullPath)) {
+ issues.push(`Missing: ${relPath}`);
+ continue;
+ }
+
+ const fileBuffer = fs.readFileSync(fullPath);
+ const actualHash = crypto.createHash("sha256").update(fileBuffer).digest("hex");
+ if (actualHash !== expectedHash) {
+ issues.push(`Modified: ${relPath}`);
+ }
+ }
+
+ if (issues.length > 0 && metadata.date && metadata.time) {
+ issues.unshift(`Backend build timestamp: ${metadata.date} ${metadata.time}`);
+ }
+
+ return {
+ ok: issues.length === 0,
+ issues: issues,
+ };
+ } catch (error) {
+ log(`Backend integrity check failed: ${error.message}`);
+ return { ok: false, issues: [error.message] };
+ }
+}
+
// allow fetching app version via ipc
ipcMain.handle("app-version", () => {
return app.getVersion();
@@ -24,12 +85,43 @@ ipcMain.handle("is-hardware-acceleration-enabled", () => {
return true; // Assume true for older versions
});
-// allow fetching integrity status (Stub for legacy)
+// allow fetching integrity status
ipcMain.handle("get-integrity-status", () => {
- return {
- backend: { ok: true, issues: ["Not supported in legacy mode"] },
- data: { ok: true, issues: ["Not supported in legacy mode"] },
- };
+ return integrityStatus;
+});
+
+// Native Notification IPC
+ipcMain.handle("show-notification", (event, { title, body, silent }) => {
+ const notification = new Notification({
+ title: title,
+ body: body,
+ silent: silent,
+ });
+ notification.show();
+
+ notification.on("click", () => {
+ if (mainWindow) {
+ mainWindow.show();
+ mainWindow.focus();
+ }
+ });
+});
+
+// Power Management IPC
+ipcMain.handle("set-power-save-blocker", (event, enabled) => {
+ if (enabled) {
+ if (activePowerSaveBlockerId === null) {
+ activePowerSaveBlockerId = powerSaveBlocker.start("prevent-app-suspension");
+ log("Power save blocker started.");
+ }
+ } else {
+ if (activePowerSaveBlockerId !== null) {
+ powerSaveBlocker.stop(activePowerSaveBlockerId);
+ activePowerSaveBlockerId = null;
+ log("Power save blocker stopped.");
+ }
+ }
+ return activePowerSaveBlockerId !== null;
});
// ignore ssl errors
@@ -80,6 +172,19 @@ ipcMain.handle("relaunch", () => {
app.exit();
});
+ipcMain.handle("relaunch-emergency", () => {
+ app.relaunch({ args: process.argv.slice(1).concat(["--emergency"]) });
+ app.exit();
+});
+
+ipcMain.handle("shutdown", () => {
+ quit();
+});
+
+ipcMain.handle("get-memory-usage", async () => {
+ return process.getProcessMemoryInfo();
+});
+
// allow showing a file path in os file manager
ipcMain.handle("showPathInFolder", (event, path) => {
shell.showItemInFolder(path);
@@ -245,6 +350,13 @@ app.whenReady().then(async () => {
log(`Found executable at: ${exe}`);
+ // Verify backend integrity before spawning
+ const exeDir = path.dirname(exe);
+ integrityStatus.backend = verifyBackendIntegrity(exeDir);
+ if (!integrityStatus.backend.ok) {
+ log(`INTEGRITY WARNING: Backend tampering detected! Issues: ${integrityStatus.backend.issues.join(", ")}`);
+ }
+
try {
// arguments we always want to pass in
const requiredArguments = [
diff --git a/package-legacy.json b/package-legacy.json
new file mode 100644
index 00000000..e2237b4d
--- /dev/null
+++ b/package-legacy.json
@@ -0,0 +1,60 @@
+{
+ "appId": "com.sudoivan.reticulummeshchat-legacy",
+ "productName": "Reticulum MeshChatX Legacy",
+ "electronVersion": "30.0.8",
+ "extraMetadata": {
+ "main": "electron/main-legacy.js"
+ },
+ "asar": true,
+ "electronFuses": {
+ "runAsNode": false,
+ "enableCookieEncryption": true,
+ "enableNodeOptionsEnvironmentVariable": false,
+ "enableNodeCliInspectArguments": false,
+ "enableEmbeddedAsarIntegrityValidation": true,
+ "onlyLoadAppFromAsar": true
+ },
+ "files": ["electron/**/*"],
+ "directories": {
+ "buildResources": "electron/build"
+ },
+ "win": {
+ "artifactName": "ReticulumMeshChat-v${version}-legacy-${os}.${ext}",
+ "target": [
+ {
+ "target": "portable"
+ },
+ {
+ "target": "nsis"
+ }
+ ],
+ "extraResources": [
+ {
+ "from": "build/exe/win32",
+ "to": "backend",
+ "filter": ["**/*"]
+ }
+ ]
+ },
+ "linux": {
+ "artifactName": "ReticulumMeshChatX-v${version}-legacy-${os}.${ext}",
+ "target": ["AppImage", "deb"],
+ "maintainer": "Sudo-Ivan",
+ "category": "Network",
+ "extraResources": [
+ {
+ "from": "build/exe/linux",
+ "to": "backend",
+ "filter": ["**/*"]
+ }
+ ]
+ },
+ "portable": {
+ "artifactName": "ReticulumMeshChatX-v${version}-legacy-${os}-portable.${ext}"
+ },
+ "nsis": {
+ "artifactName": "ReticulumMeshChatX-v${version}-legacy-${os}-installer.${ext}",
+ "oneClick": false,
+ "allowToChangeInstallationDirectory": true
+ }
+}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────